#ifndef VECTOR2D_H
#define VECTOR2D_H


#include "Prerequisites.h"
#include "Math3D.h"

#include "sqplus/sqplus.h"

using namespace SqPlus;


/** Standard 2-dimensional vector.
	@remarks
		A direction in 2D space represented as distances along the 2
		orthoganal axes (x, y). Note that positions, directions and
		scaling factors can be represented by a vector, depending on how
		you interpret the values.
*/
class Vector2D
{
public:
	float x, y;

public:
	inline Vector2D()
		: x(0.0f), y(0.0f)
	{
	}

	inline Vector2D(const float fX, const float fY )
		: x( fX ), y( fY )
	{
	}

	inline explicit Vector2D( const float scaler )
		: x( scaler), y( scaler )
	{
	}

	inline explicit Vector2D( const float afCoordinate[2] )
		: x( afCoordinate[0] ),
		  y( afCoordinate[1] )
	{
	}

	inline explicit Vector2D( const int afCoordinate[2] )
	{
		x = (float)afCoordinate[0];
		y = (float)afCoordinate[1];
	}

	inline explicit Vector2D( float* const r )
		: x( r[0] ), y( r[1] )
	{
	}

	inline Vector2D( const Vector2D& rkVector )
		: x( rkVector.x ), y( rkVector.y )
	{
	}

	inline float operator [] ( const size_t i ) const
	{
		assert( i < 2 );

		return *(&x+i);
	}

	inline float& operator [] ( const size_t i )
	{
		assert( i < 2 );

		return *(&x+i);
	}

	/// Pointer accessor for direct copying
	inline float* ptr()
	{
		return &x;
	}
	/// Pointer accessor for direct copying
	inline const float* ptr() const
	{
		return &x;
	}

	/** Assigns the value of the other vector.
		@param
			rkVector The other vector
	*/
	inline Vector2D& operator = ( const Vector2D& rkVector )
	{
		x = rkVector.x;
		y = rkVector.y;

		return *this;
	}

	inline Vector2D& operator = ( const float fScalar)
	{
		x = fScalar;
		y = fScalar;

		return *this;
	}

	inline bool operator == ( const Vector2D& rkVector ) const
	{
		return ( x == rkVector.x && y == rkVector.y );
	}

	inline bool operator != ( const Vector2D& rkVector ) const
	{
		return ( x != rkVector.x || y != rkVector.y  );
	}

	// arithmetic operations
	inline Vector2D operator + ( const Vector2D& rkVector ) const
	{
		return Vector2D(
			x + rkVector.x,
			y + rkVector.y);
	}

	inline Vector2D operator - ( const Vector2D& rkVector ) const
	{
		return Vector2D(
			x - rkVector.x,
			y - rkVector.y);
	}

	inline Vector2D operator * ( const float fScalar ) const
	{
		return Vector2D(
			x * fScalar,
			y * fScalar);
	}

	inline Vector2D operator * ( const Vector2D& rhs) const
	{
		return Vector2D(
			x * rhs.x,
			y * rhs.y);
	}

	inline Vector2D operator / ( const float fScalar ) const
	{
		assert( fScalar != 0.0 );

		float fInv = 1.0f / fScalar;

		return Vector2D(
			x * fInv,
			y * fInv);
	}

	inline Vector2D operator / ( const Vector2D& rhs) const
	{
		return Vector2D(
			x / rhs.x,
			y / rhs.y);
	}

	inline const Vector2D& operator + () const
	{
		return *this;
	}

	inline Vector2D operator - () const
	{
		return Vector2D(-x, -y);
	}

	// overloaded operators to help Vector2D
	inline friend Vector2D operator * ( const float fScalar, const Vector2D& rkVector )
	{
		return Vector2D(
			fScalar * rkVector.x,
			fScalar * rkVector.y);
	}

	inline friend Vector2D operator / ( const float fScalar, const Vector2D& rkVector )
	{
		return Vector2D(
			fScalar / rkVector.x,
			fScalar / rkVector.y);
	}

	inline friend Vector2D operator + (const Vector2D& lhs, const float rhs)
	{
		return Vector2D(
			lhs.x + rhs,
			lhs.y + rhs);
	}

	inline friend Vector2D operator + (const float lhs, const Vector2D& rhs)
	{
		return Vector2D(
			lhs + rhs.x,
			lhs + rhs.y);
	}

	inline friend Vector2D operator - (const Vector2D& lhs, const float rhs)
	{
		return Vector2D(
			lhs.x - rhs,
			lhs.y - rhs);
	}

	inline friend Vector2D operator - (const float lhs, const Vector2D& rhs)
	{
		return Vector2D(
			lhs - rhs.x,
			lhs - rhs.y);
	}
	// arithmetic updates
	inline Vector2D& operator += ( const Vector2D& rkVector )
	{
		x += rkVector.x;
		y += rkVector.y;

		return *this;
	}

	inline Vector2D& operator += ( const float fScaler )
	{
		x += fScaler;
		y += fScaler;

		return *this;
	}

	inline Vector2D& operator -= ( const Vector2D& rkVector )
	{
		x -= rkVector.x;
		y -= rkVector.y;

		return *this;
	}

	inline Vector2D& operator -= ( const float fScaler )
	{
		x -= fScaler;
		y -= fScaler;

		return *this;
	}

	inline Vector2D& operator *= ( const float fScalar )
	{
		x *= fScalar;
		y *= fScalar;

		return *this;
	}

	inline Vector2D& operator *= ( const Vector2D& rkVector )
	{
		x *= rkVector.x;
		y *= rkVector.y;

		return *this;
	}

	inline Vector2D& operator /= ( const float fScalar )
	{
		assert( fScalar != 0.0 );

		float fInv = 1.0f / fScalar;

		x *= fInv;
		y *= fInv;

		return *this;
	}

	inline Vector2D& operator /= ( const Vector2D& rkVector )
	{
		x /= rkVector.x;
		y /= rkVector.y;

		return *this;
	}

	/** Returns the length (magnitude) of the vector.
		@warning
			This operation requires a square root and is expensive in
			terms of CPU operations. If you don't need to know the exact
			length (e.g. for just comparing lengths) use squaredLength()
			instead.
	*/
	inline float length () const
	{
		return Math::Sqrt( x * x + y * y );
	}

	/** Returns the square of the length(magnitude) of the vector.
		@remarks
			This  method is for efficiency - calculating the actual
			length of a vector requires a square root, which is expensive
			in terms of the operations required. This method returns the
			square of the length of the vector, i.e. the same as the
			length but before the square root is taken. Use this if you
			want to find the longest / shortest vector without incurring
			the square root.
	*/
	inline float squaredLength () const
	{
		return x * x + y * y;
	}

	/** Calculates the dot (scalar) product of this vector with another.
		@remarks
			The dot product can be used to calculate the angle between 2
			vectors. If both are unit vectors, the dot product is the
			cosine of the angle; otherwise the dot product must be
			divided by the product of the lengths of both vectors to get
			the cosine of the angle. This result can further be used to
			calculate the distance of a point from a plane.
		@param
			vec Vector with which to calculate the dot product (together
			with this one).
		@returns
			A float representing the dot product value.
	*/
	inline float dotProduct(const Vector2D& vec) const
	{
		return x * vec.x + y * vec.y;
	}

	/** Normalises the vector.
		@remarks
			This method normalises the vector such that it's
			length / magnitude is 1. The result is called a unit vector.
		@note
			This function will not crash for zero-sized vectors, but there
			will be no changes made to their components.
		@returns The previous length of the vector.
	*/
	inline float normalize()
	{
		float fLength = Math::Sqrt( x * x + y * y);

		// Will also work for zero-sized vectors, but will change nothing
		if ( fLength > 1e-08 )
		{
			float fInvLength = 1.0f / fLength;
			x *= fInvLength;
			y *= fInvLength;
		}

		return fLength;
	}



	/** Returns a vector at a point half way between this and the passed
		in vector.
	*/
	inline Vector2D midPoint( const Vector2D& vec ) const
	{
		return Vector2D(
			( x + vec.x ) * 0.5f,
			( y + vec.y ) * 0.5f );
	}

	/** Returns true if the vector's scalar components are all greater
		that the ones of the vector it is compared against.
	*/
	inline bool operator < ( const Vector2D& rhs ) const
	{
		if( x < rhs.x && y < rhs.y )
			return true;
		return false;
	}

	/** Returns true if the vector's scalar components are all smaller
		that the ones of the vector it is compared against.
	*/
	inline bool operator > ( const Vector2D& rhs ) const
	{
		if( x > rhs.x && y > rhs.y )
			return true;
		return false;
	}

	/** Sets this vector's components to the minimum of its own and the
		ones of the passed in vector.
		@remarks
			'Minimum' in this case means the combination of the lowest
			value of x, y and z from both vectors. Lowest is taken just
			numerically, not magnitude, so -1 < 0.
	*/
	inline void makeFloor( const Vector2D& cmp )
	{
		if( cmp.x < x ) x = cmp.x;
		if( cmp.y < y ) y = cmp.y;
	}

	/** Sets this vector's components to the maximum of its own and the
		ones of the passed in vector.
		@remarks
			'Maximum' in this case means the combination of the highest
			value of x, y and z from both vectors. Highest is taken just
			numerically, not magnitude, so 1 > -3.
	*/
	inline void makeCeil( const Vector2D& cmp )
	{
		if( cmp.x > x ) x = cmp.x;
		if( cmp.y > y ) y = cmp.y;
	}

	/** Generates a vector perpendicular to this vector (eg an 'up' vector).
		@remarks
			This method will return a vector which is perpendicular to this
			vector. There are an infinite number of possibilities but this
			method will guarantee to generate one of them. If you need more
			control you should use the Quaternion class.
	*/
	inline Vector2D perpendicular(void) const
	{
		return Vector2D (-y, x);
	}
	/** Calculates the 2 dimensional cross-product of 2 vectors, which results
		in a single floating point value which is 2 times the area of the triangle.
	*/
	inline float crossProduct( const Vector2D& rkVector ) const
	{
		return x * rkVector.y - y * rkVector.x;
	}
	/** Generates a new random vector which deviates from this vector by a
		given angle in a random direction.
		@remarks
			This method assumes that the random number generator has already
			been seeded appropriately.
		@param
			angle The angle at which to deviate in radians
		@param
			up Any vector perpendicular to this one (which could generated
			by cross-product of this vector and any other non-colinear
			vector). If you choose not to provide this the function will
			derive one on it's own, however if you provide one yourself the
			function will be faster (this allows you to reuse up vectors if
			you call this method more than once)
		@returns
			A random vector which deviates from this vector by angle. This
			vector will not be normalised, normalise it if you wish
			afterwards.
	*/
	inline Vector2D randomDeviant(
		float angle) const
	{

		angle *=  Math::UnitRandom() * Math::TWO_PI;
		float cosa = cos(angle);
		float sina = sin(angle);
		return  Vector2D(cosa * x - sina * y,
						sina * x + cosa * y);
	}

	/** Returns true if this vector is zero length. */
	inline bool isZeroLength(void) const
	{
		float sqlen = (x * x) + (y * y);
		return (sqlen < (1e-06 * 1e-06));

	}

	/** As normalise, except that this vector is unaffected and the
		normalised vector is returned as a copy. */
	inline Vector2D normalisedCopy(void) const
	{
		Vector2D ret = *this;
		ret.normalize();
		return ret;
	}

	/** Calculates a reflection vector to the plane with the given normal .
	@remarks NB assumes 'this' is pointing AWAY FROM the plane, invert if it is not.
	*/
	inline Vector2D reflect(const Vector2D& normal) const
	{
		return Vector2D( *this - ( 2 * this->dotProduct(normal) * normal ) );
	}

	// special points
	static const Vector2D ZERO;
	static const Vector2D UNIT_X;
	static const Vector2D UNIT_Y;
	static const Vector2D NEGATIVE_UNIT_X;
	static const Vector2D NEGATIVE_UNIT_Y;
	static const Vector2D UNIT_SCALE;

	/** Function for writing to a stream.
	*/
	inline friend std::ostream& operator <<
		( std::ostream& o, const Vector2D& v )
	{
		o << "Vector2D(" << v.x << ", " << v.y <<  ")";
		return o;
	}

};

//must be declared here
DECLARE_INSTANCE_TYPE(Vector2D)

#endif
